/*
 * anima/mm.cc - Generic memory management functions.
 * Copyright (C) 2007  Mikhail Vorozhtsov
 * See the LICENSE section of the README file for details.
 */

/* $Id$ */

#include HEADER (anima,mm.hh)
#include HEADER (anima,pdb.hh)
#include HEADER (anima,console.hh)
#include HEADER (utils,memory.hh)
#include HEADER (utils,minmax.hh)

START_NAMESPACE (anima)

bool mm::_initialized_p;

bool
mm::init ()
{
  if (!ANIMA_ARCH::mm::init ())
    return false;

  _initialized_p = true;

  // Remap firmware memory.
  if (!firmware::remap ())
    {
      console::error ("Cannot remap firmware memory.");
      return false;
    }

  // Add memory regions to PDB.
  if (!pdb::fill ())
    {
      console::error ("Cannot fill PDB.");
      return false;
    }

  return true;
}

u64_t
mm::length (u64_t paddr)
{
  u64_t p = CUT (paddr, PAGE_SIZE); 
  u64_t offset = paddr - p;
  u64_t result = 0;

  for (; true; p += PAGE_SIZE, offset = 0)
    {
      const void* page
        = map (p, (tte_t::flag_t) (tte_t::FLAG_DEFAULTS & ~tte_t::FLAG_WRITE));

      if (!page)
        return 0;

      for (const void *str = page + offset; str < page + PAGE_SIZE; ++str)
        if (*(const char *) str == 0)
          {
            mm::unmap ((u64_t) page);
            return result;
          }
        else
          ++result;

      mm::unmap ((u64_t) page);
    }

  return result;
}

bool
mm::cp (u64_t paddr, void *dst, u64_t size)
{
  u64_t p = CUT (paddr, PAGE_SIZE); 
  u64_t offset = paddr - p;

  for (; size > 0; p += PAGE_SIZE, offset = 0)
    {
      const void* src
        = map (p, (tte_t::flag_t) (tte_t::FLAG_DEFAULTS & ~tte_t::FLAG_WRITE));

      if (!src)
        return false;

      u64_t n = utils::min (size, PAGE_SIZE - offset);

      utils::cp (src + offset, dst, n);

      mm::unmap ((u64_t) src);

      dst += n;
      size -= n;
    }

  return true;
}

bool
mm::cp (const void *src, u64_t paddr, u64_t size)
{
  u64_t p = CUT (paddr, PAGE_SIZE); 
  u64_t offset = paddr - p;

  for (; size > 0; p += PAGE_SIZE, offset = 0)
    {
      void* dst = map (p);

      if (!dst)
        return false;

      u64_t n = utils::min (size, PAGE_SIZE - offset);

      utils::cp (src, dst + offset, n);

      mm::unmap ((u64_t) dst);

      src += n;
      size -= n;
    }

  return true;
}

END_NAMESPACE

